import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/crypto";
import { describe } from "vitest";
import { STACK_BACKEND_BASE_URL, it } from "../../../../helpers";
import { localhostUrl } from "../../../../helpers/ports";
import { Auth, InternalProjectKeys, Project, Team, Webhook, backendContext, bumpEmailAddress, createMailbox, niceBackendFetch } from "../../../backend-helpers";

describe("without project access", () => {
  backendContext.set({
    projectKeys: "no-project",
  });

  it("should not be able to read own user", async ({ expect }) => {
    await backendContext.with({
      projectKeys: InternalProjectKeys,
    }, async () => {
      await Auth.Otp.signIn();
    });
    const response = await niceBackendFetch("/api/v1/users/me");
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "ACCESS_TYPE_REQUIRED",
          "error": deindent\`
            You must specify an access level for this Stack project. Make sure project API keys are provided (eg. x-stack-publishable-client-key) and you set the x-stack-access-type header to 'client', 'server', or 'admin'.
            
            For more information, see the docs on REST API authentication: https://docs.stack-auth.com/rest-api/overview#authentication
          \`,
        },
        "headers": Headers {
          "x-stack-known-error": "ACCESS_TYPE_REQUIRED",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to list users", async ({ expect }) => {
    await Project.createAndSwitch();
    const response = await niceBackendFetch("/api/v1/users");
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "ACCESS_TYPE_REQUIRED",
          "error": deindent\`
            You must specify an access level for this Stack project. Make sure project API keys are provided (eg. x-stack-publishable-client-key) and you set the x-stack-access-type header to 'client', 'server', or 'admin'.
            
            For more information, see the docs on REST API authentication: https://docs.stack-auth.com/rest-api/overview#authentication
          \`,
        },
        "headers": Headers {
          "x-stack-known-error": "ACCESS_TYPE_REQUIRED",
          <some fields may have been hidden>,
        },
      }
    `);
  });
});

describe("with client access", () => {
  it("should not be able to read own user if not signed in", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "CANNOT_GET_OWN_USER_WITHOUT_USER",
          "error": "You have specified 'me' as a userId, but did not provide authentication for a user.",
        },
        "headers": Headers {
          "x-stack-known-error": "CANNOT_GET_OWN_USER_WITHOUT_USER",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it.todo("should not be able to read own user if access token uses an incorrect signature", async ({ expect }) => {
    // TODO we should hardcode an access token generated with a different signature here
    backendContext.set({ userAuth: { accessToken: "replace this with an access token that uses a different signature" } });
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 401,
        "body": {
          "code": "UNPARSABLE_ACCESS_TOKEN",
          "error": "Access token is not parsable.",
        },
        "headers": Headers {
          "x-stack-known-error": "UNPARSABLE_ACCESS_TOKEN",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it.todo("should not be able to read own user if access token is expired", async ({ expect }) => {
    // TODO instead of hardcoding an access token here, we should generate one that is short-lived and wait for it to expire
    // this test will fail in some environments because the signature is incorrect
    backendContext.set({ userAuth: { ...backendContext.value.userAuth, accessToken: "eyJhbGciOiJFUzI1NiIsImtpZCI6IkVYVkNzT01NRkpBMiJ9.eyJzdWIiOiIzM2U3YzA0My1kMmQxLTQxODctYWNkMy1mOTFiNWVkNjRiNDYiLCJpc3MiOiJodHRwczovL2FjY2Vzcy10b2tlbi5qd3Qtc2lnbmF0dXJlLnN0YWNrLWF1dGguY29tIiwiaWF0IjoxNzM4Mzc0OTU4LCJhdWQiOiJpbnRlcm5hbCIsImV4cCI6MTczODM3NDk4OH0.8USE-ELS4IYjFbzA5yNppNKKQGhdNQ0cUUBW7DMG8xHSfqEGw0Bm19u5uUZV6j0tGZypxRbIftgGaVdBRAOCig" } });
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 401,
        "body": {
          "code": "ACCESS_TOKEN_EXPIRED",
          "details": { "expired_at_millis": 1738374988000 },
          "error": "Access token has expired. Please refresh it and try again. (The access token expired at 2025-02-01T01:56:28.000Z.)",
        },
        "headers": Headers {
          "x-stack-known-error": "ACCESS_TOKEN_EXPIRED",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should be able to read own user if signed in", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
          },
          "selected_team_id": "<stripped UUID>",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to read own user if signed in even without refresh token", async ({ expect }) => {
    await Auth.Otp.signIn();
    backendContext.set({ userAuth: { ...backendContext.value.userAuth, refreshToken: undefined } });
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
          },
          "selected_team_id": "<stripped UUID>",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to read own user without access token even if refresh token is given", async ({ expect }) => {
    await Auth.Otp.signIn();
    backendContext.set({ userAuth: { ...backendContext.value.userAuth, accessToken: undefined } });
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "CANNOT_GET_OWN_USER_WITHOUT_USER",
          "error": "You have specified 'me' as a userId, but did not provide authentication for a user.",
        },
        "headers": Headers {
          "x-stack-known-error": "CANNOT_GET_OWN_USER_WITHOUT_USER",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should return access token invalid error when reading own user with invalid access token", async ({ expect }) => {
    await Auth.Otp.signIn();
    backendContext.set({ userAuth: { ...backendContext.value.userAuth, accessToken: "12341234" } });
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 401,
        "body": {
          "code": "UNPARSABLE_ACCESS_TOKEN",
          "error": "Access token is not parsable.",
        },
        "headers": Headers {
          "x-stack-known-error": "UNPARSABLE_ACCESS_TOKEN",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should be able to update own user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response1 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        display_name: "John Doe",
      },
    });
    expect(response1).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "John Doe",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
          },
          "selected_team_id": "<stripped UUID>",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const response2 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        client_metadata: { key: "value" },
      },
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": { "key": "value" },
          "client_read_only_metadata": null,
          "display_name": "John Doe",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
          },
          "selected_team_id": "<stripped UUID>",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it.todo("should be able to set own profile image URL with an image HTTP URL, and the new profile image URL should be a different HTTP URL on our storage service");

  it.todo("should be able to set own profile image URL with a base64 data URL, and the new profile image URL should be a different HTTP URL on our storage service");

  it.todo("should not be able to set own profile image URL with a file: protocol URL");

  it.todo("should not be able to set own profile image URL to a localhost/non-public URL");

  it("should not be able to set own server_metadata", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        display_name: "Johnny Doe",
        server_metadata: { "key": "value" },
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "SCHEMA_ERROR",
          "details": {
            "message": deindent\`
              Request validation failed on PATCH /api/v1/users/me:
                - body contains unknown properties: server_metadata
            \`,
          },
          "error": deindent\`
            Request validation failed on PATCH /api/v1/users/me:
              - body contains unknown properties: server_metadata
          \`,
        },
        "headers": Headers {
          "x-stack-known-error": "SCHEMA_ERROR",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to delete own user if project is not configured to allow it", async ({ expect }) => {
    await Project.createAndSwitch({
      config: {
        client_user_deletion_enabled: false,
        magic_link_enabled: true,
      },
    });

    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "DELETE",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": "Client user deletion is not enabled for this project",
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to delete own user if project is configured to allow it", async ({ expect }) => {
    await Project.createAndSwitch({
      config: {
        client_user_deletion_enabled: true,
        magic_link_enabled: true,
      },
    });

    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "DELETE",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": { "success": true },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to create a user", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "client",
      method: "POST",
      body: {},
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 401,
        "body": {
          "code": "INSUFFICIENT_ACCESS_TYPE",
          "details": {
            "actual_access_type": "client",
            "allowed_access_types": [
              "server",
              "admin",
            ],
          },
          "error": "The x-stack-access-type header must be 'server' or 'admin', but was 'client'.",
        },
        "headers": Headers {
          "x-stack-known-error": "INSUFFICIENT_ACCESS_TYPE",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should set own display name to null when set to the empty string", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response1 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        display_name: "John Doe",
      },
    });
    expect(response1).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "John Doe",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
          },
          "selected_team_id": "<stripped UUID>",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const response2 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        display_name: "",
      },
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
          },
          "selected_team_id": "<stripped UUID>",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update totp_secret_base64 to valid base64", async ({ expect }) => {
    await Auth.Otp.signIn();
    const secret = generateSecureRandomString(32);
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        totp_secret_base64: "ZXhhbXBsZSB2YWx1ZQ==",
      },
    });
    expect(response.status).toEqual(200);
  });

  it("should not be able to update totp_secret_base64 to invalid base64", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        totp_secret_base64: "not-valid-base64",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "SCHEMA_ERROR",
          "details": {
            "message": deindent\`
              Request validation failed on PATCH /api/v1/users/me:
                - body.totp_secret_base64 is not valid base64
            \`,
          },
          "error": deindent\`
            Request validation failed on PATCH /api/v1/users/me:
              - body.totp_secret_base64 is not valid base64
          \`,
        },
        "headers": Headers {
          "x-stack-known-error": "SCHEMA_ERROR",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to list users", async ({ expect }) => {
    await Project.createAndSwitch();
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 401,
        "body": {
          "code": "INSUFFICIENT_ACCESS_TYPE",
          "details": {
            "actual_access_type": "client",
            "allowed_access_types": [
              "server",
              "admin",
            ],
          },
          "error": "The x-stack-access-type header must be 'server' or 'admin', but was 'client'.",
        },
        "headers": Headers {
          "x-stack-known-error": "INSUFFICIENT_ACCESS_TYPE",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to read a user", async ({ expect }) => {
    await Auth.Otp.signIn();
    backendContext.set({
      userAuth: null,
    });
    const response = await niceBackendFetch("/api/v1/users/123", {
      accessType: "client",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 401,
        "body": {
          "code": "INSUFFICIENT_ACCESS_TYPE",
          "details": {
            "actual_access_type": "client",
            "allowed_access_types": [
              "server",
              "admin",
            ],
          },
          "error": "The x-stack-access-type header must be 'server' or 'admin', but was 'client'.",
        },
        "headers": Headers {
          "x-stack-known-error": "INSUFFICIENT_ACCESS_TYPE",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should be able to update own client metadata", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        client_metadata: { key: "value" },
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": { "key": "value" },
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
          },
          "selected_team_id": "<stripped UUID>",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to update own client read-only metadata", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        client_read_only_metadata: { key: "value" },
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "SCHEMA_ERROR",
          "details": {
            "message": deindent\`
              Request validation failed on PATCH /api/v1/users/me:
                - body contains unknown properties: client_read_only_metadata
            \`,
          },
          "error": deindent\`
            Request validation failed on PATCH /api/v1/users/me:
              - body contains unknown properties: client_read_only_metadata
          \`,
        },
        "headers": Headers {
          "x-stack-known-error": "SCHEMA_ERROR",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to update profile image url", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        profile_image_url: localhostUrl("01", "/open-graph-image.png"),
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": "Invalid profile image URL",
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update profile image url with base64", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        profile_image_url: "",
      },
    });
    expect(response.body.profile_image_url).toMatchInlineSnapshot(`"http://localhost:<$NEXT_PUBLIC_STACK_PORT_PREFIX>21/stack-storage/user-profile-images/<stripped UUID>.gif"`);
  });

  it("should not be able to update profile image url with invalid base64", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        profile_image_url: "",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": "Invalid profile image URL",
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update selected team", async ({ expect }) => {
    await Project.createAndSwitch({ config: { magic_link_enabled: true } });
    await Auth.Otp.signIn();
    const { teamId: team1Id } = await Team.createWithCurrentAsCreator({});
    const { teamId: team2Id } = await Team.createWithCurrentAsCreator({});
    const response1 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
    });
    expect(response1.body.selected_team_id).toEqual(null);
    const response2 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        selected_team_id: team1Id,
      },
    });
    expect(response2.body.selected_team_id).toEqual(team1Id);
    const response3 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        selected_team_id: team2Id,
      },
    });
    expect(response3.body.selected_team_id).toEqual(team2Id);
    const response4 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "client",
      method: "PATCH",
      body: {
        selected_team_id: null,
      },
    });
    expect(response4.body.selected_team_id).toEqual(null);
  });
});

describe("with server access", () => {
  it("should be able to read own user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update own user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        display_name: "John Doe",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "John Doe",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to delete own user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "DELETE",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": { "success": true },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to list users", async ({ expect }) => {
    await Project.createAndSwitch({ config: { magic_link_enabled: true } });
    await Auth.Otp.signIn();

    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "is_paginated": true,
          "items": [
            {
              "auth_with_email": true,
              "client_metadata": null,
              "client_read_only_metadata": null,
              "display_name": null,
              "has_password": false,
              "id": "<stripped UUID>",
              "is_anonymous": false,
              "last_active_at_millis": <stripped field 'last_active_at_millis'>,
              "oauth_providers": [],
              "otp_auth_enabled": true,
              "passkey_auth_enabled": false,
              "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
              "primary_email_auth_enabled": true,
              "primary_email_verified": true,
              "profile_image_url": null,
              "requires_totp_mfa": false,
              "selected_team": null,
              "selected_team_id": null,
              "server_metadata": null,
              "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
            },
          ],
          "pagination": { "next_cursor": null },
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("lists users with pagination", async ({ expect }) => {
    await Project.createAndSwitch({ config: { magic_link_enabled: true } });
    for (let i = 0; i < 5; i++) {
      await bumpEmailAddress();
      await Auth.Otp.signIn();
    }
    const allResponse = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
    });

    const response1 = await niceBackendFetch("/api/v1/users?limit=2", {
      accessType: "server",
    });
    expect(response1.body.pagination.next_cursor).toBeDefined();

    const response2 = await niceBackendFetch(`/api/v1/users?limit=3&cursor=${response1.body.pagination.next_cursor}`, {
      accessType: "server",
    });
    expect(response2.body.pagination.next_cursor).toBeDefined();

    // check if response 1 + response 2 = allResponse
    expect(response1.body.items.length + response2.body.items.length).toEqual(allResponse.body.items.length);
    const allUserIds = new Set(allResponse.body.items.map((user: any) => user.id));
    const concatenatedUserIds = new Set([...response1.body.items.map((user: any) => user.id), ...response2.body.items.map((user: any) => user.id)]);
    expect(concatenatedUserIds).toEqual(allUserIds);
  });

  it("should be able to read a user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const signedInResponse = (await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
    }));
    const userId = signedInResponse.body.id;
    backendContext.set({
      userAuth: null,
    });
    const response = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    expect(response.body.primary_email).toEqual(backendContext.value.mailbox.emailAddress);
  });

  it("should be able to create a user", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {},
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": null,
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to create a user with email auth enabled", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        display_name: "John Dough",
        server_metadata: "test",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "John Dough",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": "test",
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to create a user with an email that doesn't match the strict email schema", async ({ expect }) => {
    // This test is to ensure that we don't break existing users who have an email that doesn't match the strict email
    // schema.
    // The frontend no longer allows those emails, but some users may still have them in their accounts and we should
    // continue to support them.
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: "invalid_email@gmai"
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "invalid_email@gmai",
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to create a user with a password and sign in with it", async ({ expect }) => {
    const password = generateSecureRandomString();
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        password,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": true,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const signInResponse = await Auth.Password.signInWithEmail({ password });
    expect(signInResponse.signInResponse).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "access_token": <stripped field 'access_token'>,
          "refresh_token": <stripped field 'refresh_token'>,
          "user_id": "<stripped UUID>",
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to create a user with a password hash and sign in with it", async ({ expect }) => {
    const password = "hello-world";
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        password_hash: "$2a$13$TVyY/gpw9Db/w1fBeJkCgeNg2Rae2JfNqrPnSAKtj.ufAO5cVF13.",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": true,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const signInResponse = await Auth.Password.signInWithEmail({ password });
    expect(signInResponse.signInResponse).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "access_token": <stripped field 'access_token'>,
          "refresh_token": <stripped field 'refresh_token'>,
          "user_id": "<stripped UUID>",
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to create an anonymous user", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        is_anonymous: true,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": true,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": null,
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to make an anonymous user non-anonymous", async ({ expect }) => {
    await Auth.Anonymous.signUp();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        is_anonymous: false,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": null,
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "Personal Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to make a non-anonymous user anonymous", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {},
    });
    const userId = response.body.id;
    const response2 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
      method: "PATCH",
      body: {
        is_anonymous: true,
      },
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "SCHEMA_ERROR",
          "details": {
            "message": deindent\`
              Request validation failed on PATCH /api/v1/users/<stripped UUID>:
                - body.is_anonymous must be one of the following values: false
            \`,
          },
          "error": deindent\`
            Request validation failed on PATCH /api/v1/users/<stripped UUID>:
              - body.is_anonymous must be one of the following values: false
          \`,
        },
        "headers": Headers {
          "x-stack-known-error": "SCHEMA_ERROR",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to create a user when both password and password hash are provided", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        password: "hello-world",
        password_hash: "$2a$13$TVyY/gpw9Db/w1fBeJkCgeNg2Rae2JfNqrPnSAKtj.ufAO5cVF13.",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": "Cannot set both password and password_hash at the same time.",
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to create a user with a password hash that has too many rounds", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        password_hash: "$2a$17$VIhIOofSMqGdGlL4wzE//e.77dAQGqNtF/1dT7bqCrVtQuInWy2qi",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": "Invalid password hash. Make sure it's a supported algorithm in Modular Crypt Format.",
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to create a user without primary email but with email auth enabled", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email_auth_enabled: true,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": "primary_email_auth_enabled cannot be true without primary_email",
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to create a user with email auth enabled if the email already exists with email auth enabled", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const response2 = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
      },
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 409,
        "body": {
          "code": "USER_EMAIL_ALREADY_EXISTS",
          "details": {
            "email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
            "would_work_if_email_was_verified": false,
          },
          "error": "A user with email \\"default-mailbox--<stripped UUID>@stack-generated.example.com\\" already exists.",
        },
        "headers": Headers {
          "x-stack-known-error": "USER_EMAIL_ALREADY_EXISTS",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should be able to create a user with email auth enabled if the email already exists but without email auth enabled", async ({ expect }) => {
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const password = generateSecureRandomString();
    const response2 = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        password: password,
      },
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": true,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const signInResponse = await Auth.Password.signInWithEmail({ password });
    expect(signInResponse.signInResponse).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "access_token": <stripped field 'access_token'>,
          "refresh_token": <stripped field 'refresh_token'>,
          "user_id": "<stripped UUID>",
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to create a user with email auth disabled even if the email already exists with email auth enabled", async ({ expect }) => {
    const password = generateSecureRandomString();
    const response2 = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        password: password,
      },
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": true,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const response = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 201,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": null,
          "selected_team_id": null,
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const signInResponse = await Auth.Password.signInWithEmail({ password });
    expect(signInResponse.signInResponse).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "access_token": <stripped field 'access_token'>,
          "refresh_token": <stripped field 'refresh_token'>,
          "user_id": "<stripped UUID>",
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update a user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const signedInResponse = (await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
    }));
    const userId = signedInResponse.body.id;
    backendContext.set({
      userAuth: null,
    });
    const response1 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
      method: "PATCH",
      body: {
        display_name: "John Doe",
        server_metadata: { key: "value" },
      },
    });
    expect(response1).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "John Doe",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": { "key": "value" },
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const response2 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "John Doe",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": { "key": "value" },
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update own user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response1 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        display_name: "John Doe",
        server_metadata: { key: "value" },
      },
    });
    expect(response1).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "John Doe",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": { "key": "value" },
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update a user's password, signing them out, and sign in with it again", async ({ expect }) => {
    const password = "this-is-some-password";
    await Auth.Otp.signIn();
    const response1 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        password,
      },
    });
    expect(response1).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": true,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    backendContext.set({
      userAuth: {
        ...backendContext.value.userAuth,
        accessToken: undefined,
      },
    });
    await Auth.expectToBeSignedOut();
    await Auth.Password.signInWithEmail({ password });
    await Auth.expectToBeSignedIn();
  });

  it("should be able to delete a user", async ({ expect }) => {
    await Auth.Otp.signIn();
    const signedInResponse = (await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
    }));
    const userId = signedInResponse.body.id;
    backendContext.set({
      userAuth: null,
    });
    const response1 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
      method: "DELETE",
    });
    expect(response1).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": { "success": true },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
    const response2 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 404,
        "body": {
          "code": "USER_NOT_FOUND",
          "error": "User not found.",
        },
        "headers": Headers {
          "x-stack-known-error": "USER_NOT_FOUND",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should be able to update all metadata fields", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        client_metadata: { key: "client value" },
        client_read_only_metadata: { key: "client read only value" },
        server_metadata: { key: "server value" },
      },
    });

    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": { "key": "client value" },
          "client_read_only_metadata": { "key": "client read only value" },
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": { "key": "server value" },
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update profile image url", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        profile_image_url: localhostUrl("01", "/open-graph-image.png"),
      },
    });
    expect(response.body.profile_image_url).toMatchInlineSnapshot(`"http://localhost:<$NEXT_PUBLIC_STACK_PORT_PREFIX>01/open-graph-image.png"`);
  });

  it("should be able to update primary email", async ({ expect }) => {
    await Auth.Otp.signIn();
    const mailbox = createMailbox();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email: mailbox.emailAddress,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": "mailbox-1--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": true,
          "primary_email_verified": true,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should be able to update primary email and sign-in with the new email", async ({ expect }) => {
    await Auth.Password.signUpWithEmail({ password: "password123" });
    const mailbox = createMailbox();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email: mailbox.emailAddress,
      },
    });
    expect(response.body.primary_email).toEqual(mailbox.emailAddress);

    backendContext.set({
      mailbox,
    });
    await Auth.Password.signInWithEmail({ password: "password123" });
    expect(response.body.primary_email).toEqual(mailbox.emailAddress);
  });

  it("should be able to remove primary email", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email: null,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": true,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": null,
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": true,
          "passkey_auth_enabled": false,
          "primary_email": null,
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "default-mailbox--<stripped UUID>@stack-generated.example.com's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });

  it("should not be able to update primary email to an email already in use for auth by someone else", async ({ expect }) => {
    await Auth.Otp.signIn();
    const primaryEmail = backendContext.value.mailbox.emailAddress;
    await Auth.signOut();
    await bumpEmailAddress();
    await Auth.Password.signUpWithEmail({ password: "password123" });
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email: primaryEmail,
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 409,
        "body": {
          "code": "USER_EMAIL_ALREADY_EXISTS",
          "details": {
            "email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
            "would_work_if_email_was_verified": false,
          },
          "error": "A user with email \\"default-mailbox--<stripped UUID>@stack-generated.example.com\\" already exists.",
        },
        "headers": Headers {
          "x-stack-known-error": "USER_EMAIL_ALREADY_EXISTS",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to set profile image url to empty string", async ({ expect }) => {
    await Auth.Otp.signIn();
    const response = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        profile_image_url: "",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 400,
        "body": {
          "code": "SCHEMA_ERROR",
          "details": {
            "message": deindent\`
              Request validation failed on PATCH /api/v1/users/me:
                - body.profile_image_url is not a valid URL
            \`,
          },
          "error": deindent\`
            Request validation failed on PATCH /api/v1/users/me:
              - body.profile_image_url is not a valid URL
          \`,
        },
        "headers": Headers {
          "x-stack-known-error": "SCHEMA_ERROR",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should not be able to sign up with an email already in use for auth", async ({ expect }) => {
    await Auth.Password.signUpWithEmail({ password: "password123" });
    const response = await niceBackendFetch("/api/v1/auth/password/sign-up", {
      method: "POST",
      accessType: "client",
      body: {
        email: backendContext.value.mailbox.emailAddress,
        password: "password123",
        verification_callback_url: "http://localhost:12345/some-callback-url",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 409,
        "body": {
          "code": "USER_EMAIL_ALREADY_EXISTS",
          "details": {
            "email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
            "would_work_if_email_was_verified": false,
          },
          "error": "A user with email \\"default-mailbox--<stripped UUID>@stack-generated.example.com\\" already exists.",
        },
        "headers": Headers {
          "x-stack-known-error": "USER_EMAIL_ALREADY_EXISTS",
          <some fields may have been hidden>,
        },
      }
    `);
  });

  it("should be able to sign up with an email already in use for auth in a different project", async ({ expect }) => {
    await Auth.Password.signUpWithEmail({ password: "password123" });
    await Project.createAndSwitch({});
    const response = await niceBackendFetch("/api/v1/auth/password/sign-up", {
      method: "POST",
      accessType: "client",
      body: {
        email: backendContext.value.mailbox.emailAddress,
        password: "password123",
        verification_callback_url: "http://localhost:12345/some-callback-url",
      },
    });
    expect(response).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "access_token": <stripped field 'access_token'>,
          "refresh_token": <stripped field 'refresh_token'>,
          "user_id": "<stripped UUID>",
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);
  });


  it("should trigger user webhook when a user is created", async ({ expect }) => {
    const { projectId, svixToken, endpointId } = await Webhook.createProjectWithEndpoint();

    const createUserResponse = await niceBackendFetch(new URL("/api/v1/users", STACK_BACKEND_BASE_URL), {
      method: "POST",
      accessType: "server",
      body: {
        primary_email: "test@example.com",
      },
    });

    expect(createUserResponse.status).toBe(201);

    const attemptResponse = await Webhook.findWebhookAttempt(projectId, endpointId, svixToken, event => true);

    expect(attemptResponse).toMatchInlineSnapshot(`
      {
        "channels": null,
        "eventId": null,
        "eventType": "user.created",
        "id": "<stripped svix message id>",
        "payload": {
          "data": {
            "auth_with_email": false,
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": null,
            "has_password": false,
            "id": "<stripped UUID>",
            "is_anonymous": false,
            "last_active_at_millis": <stripped field 'last_active_at_millis'>,
            "oauth_providers": [],
            "otp_auth_enabled": false,
            "passkey_auth_enabled": false,
            "primary_email": "test@example.com",
            "primary_email_auth_enabled": false,
            "primary_email_verified": false,
            "profile_image_url": null,
            "requires_totp_mfa": false,
            "selected_team": null,
            "selected_team_id": null,
            "server_metadata": null,
            "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
          },
          "type": "user.created",
        },
        "timestamp": <stripped field 'timestamp'>,
      }
    `);
  });

  it("should trigger user webhook when a user is updated", async ({ expect }) => {
    const { projectId, svixToken, endpointId } = await Webhook.createProjectWithEndpoint();

    const createUserResponse = await niceBackendFetch("/api/v1/users", {
      method: "POST",
      accessType: "server",
      body: {
        primary_email: "test@example.com",
      },
    });

    expect(createUserResponse.status).toBe(201);
    const userId = createUserResponse.body.id;

    const updateUserResponse = await niceBackendFetch(`/api/v1/users/${userId}`, {
      method: "PATCH",
      accessType: "server",
      body: {
        display_name: "Test User"
      }
    });

    expect(updateUserResponse.status).toBe(200);

    const userUpdatedEvent = await Webhook.findWebhookAttempt(projectId, endpointId, svixToken, event => event.eventType === "user.updated");

    expect(userUpdatedEvent).toMatchInlineSnapshot(`
      {
        "channels": null,
        "eventId": null,
        "eventType": "user.updated",
        "id": "<stripped svix message id>",
        "payload": {
          "data": {
            "auth_with_email": false,
            "client_metadata": null,
            "client_read_only_metadata": null,
            "display_name": "Test User",
            "has_password": false,
            "id": "<stripped UUID>",
            "is_anonymous": false,
            "last_active_at_millis": <stripped field 'last_active_at_millis'>,
            "oauth_providers": [],
            "otp_auth_enabled": false,
            "passkey_auth_enabled": false,
            "primary_email": "test@example.com",
            "primary_email_auth_enabled": false,
            "primary_email_verified": false,
            "profile_image_url": null,
            "requires_totp_mfa": false,
            "selected_team": null,
            "selected_team_id": null,
            "server_metadata": null,
            "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
          },
          "type": "user.updated",
        },
        "timestamp": <stripped field 'timestamp'>,
      }
    `);
  });

  it("should trigger user webhook when a user is deleted", async ({ expect }) => {
    const { projectId, svixToken, endpointId } = await Webhook.createProjectWithEndpoint();

    const createUserResponse = await niceBackendFetch(new URL("/api/v1/users", STACK_BACKEND_BASE_URL), {
      method: "POST",
      accessType: "server",
      body: {
        primary_email: "test@example.com",
      },
    });

    expect(createUserResponse.status).toBe(201);
    const userId = createUserResponse.body.id;

    const deleteUserResponse = await niceBackendFetch(new URL(`/api/v1/users/${userId}`, STACK_BACKEND_BASE_URL), {
      method: "DELETE",
      accessType: "server",
    });

    expect(deleteUserResponse.status).toBe(200);

    const userDeletedEvent = await Webhook.findWebhookAttempt(projectId, endpointId, svixToken, event => event.eventType === "user.deleted");

    expect(userDeletedEvent).toMatchInlineSnapshot(`
      {
        "channels": null,
        "eventId": null,
        "eventType": "user.deleted",
        "id": "<stripped svix message id>",
        "payload": {
          "data": {
            "id": "<stripped UUID>",
            "teams": [],
          },
          "type": "user.deleted",
        },
        "timestamp": <stripped field 'timestamp'>,
      }
    `);
  });

  it("should be able to properly disable primary_email_auth_enabled", async ({ expect }) => {
    // Test for the fix where primary_email_auth_enabled couldn't be disabled properly
    // due to an OR operator issue that always kept it true

    // Create a user with email auth enabled
    const response1 = await niceBackendFetch("/api/v1/users", {
      accessType: "server",
      method: "POST",
      body: {
        primary_email: backendContext.value.mailbox.emailAddress,
        primary_email_auth_enabled: true,
        display_name: "Test User",
      },
    });
    expect(response1.status).toEqual(201);
    expect(response1.body.primary_email_auth_enabled).toEqual(true);
    const userId = response1.body.id;

    // Update the user to disable email auth
    const response2 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email_auth_enabled: false,
      },
    });
    expect(response2).toMatchInlineSnapshot(`
      NiceResponse {
        "status": 200,
        "body": {
          "auth_with_email": false,
          "client_metadata": null,
          "client_read_only_metadata": null,
          "display_name": "Test User",
          "has_password": false,
          "id": "<stripped UUID>",
          "is_anonymous": false,
          "last_active_at_millis": <stripped field 'last_active_at_millis'>,
          "oauth_providers": [],
          "otp_auth_enabled": false,
          "passkey_auth_enabled": false,
          "primary_email": "default-mailbox--<stripped UUID>@stack-generated.example.com",
          "primary_email_auth_enabled": false,
          "primary_email_verified": false,
          "profile_image_url": null,
          "requires_totp_mfa": false,
          "selected_team": {
            "client_metadata": null,
            "client_read_only_metadata": null,
            "created_at_millis": <stripped field 'created_at_millis'>,
            "display_name": "Test User's Team",
            "id": "<stripped UUID>",
            "profile_image_url": null,
            "server_metadata": null,
          },
          "selected_team_id": "<stripped UUID>",
          "server_metadata": null,
          "signed_up_at_millis": <stripped field 'signed_up_at_millis'>,
        },
        "headers": Headers { <some fields may have been hidden> },
      }
    `);

    // Verify the change persisted by reading the user again
    const response3 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
    });
    expect(response3.status).toEqual(200);
    expect(response3.body.primary_email_auth_enabled).toEqual(false);
    expect(response3.body.auth_with_email).toEqual(false);

    // Test that we can re-enable it
    const response4 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email_auth_enabled: true,
      },
    });
    expect(response4.status).toEqual(200);
    expect(response4.body.primary_email_auth_enabled).toEqual(true);
    expect(response4.body.auth_with_email).toEqual(false); // Still false because no password/otp is set

    // Verify re-enabling persisted
    const response5 = await niceBackendFetch("/api/v1/users/" + userId, {
      accessType: "server",
    });
    expect(response5.status).toEqual(200);
    expect(response5.body.primary_email_auth_enabled).toEqual(true);
  });

  it("should be able to disable primary_email_auth_enabled on current user", async ({ expect }) => {
    // Test the same functionality when updating the current user via /me endpoint
    await Auth.Otp.signIn();

    // First verify the user has email auth enabled (from OTP sign in)
    const initialResponse = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
    });
    expect(initialResponse.status).toEqual(200);
    expect(initialResponse.body.primary_email_auth_enabled).toEqual(true);

    // Disable email auth on current user
    const response1 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email_auth_enabled: false,
      },
    });
    expect(response1.status).toEqual(200);
    expect(response1.body.primary_email_auth_enabled).toEqual(false);
    expect(response1.body.auth_with_email).toEqual(true); // May still be true due to existing auth methods

    // Verify the change persisted by reading the user again
    const response2 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
    });
    expect(response2.status).toEqual(200);
    expect(response2.body.primary_email_auth_enabled).toEqual(false);

    // Re-enable email auth
    const response3 = await niceBackendFetch("/api/v1/users/me", {
      accessType: "server",
      method: "PATCH",
      body: {
        primary_email_auth_enabled: true,
      },
    });
    expect(response3.status).toEqual(200);
    expect(response3.body.primary_email_auth_enabled).toEqual(true);
  });
});

it.todo("creating a new user with an OAuth provider ID that does not exist should fail");
it.todo("creating a new user with password enabled when password sign in is disabled in the config should fail");
